/* 整理自 https://stackoverflow.com/questions/31646466/how-to-send-signal-from-kernel-to-user-space */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/mm.h>
#include <linux/namei.h>
#include <linux/moduleparam.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <asm/siginfo.h>    //siginfo
#include <linux/rcupdate.h> //rcu_read_lock
#include <linux/sched.h>    //find_task_by_pid_type

static int pid = 0; // Stores application PID in user space
module_param(pid, int, S_IRUSR);
#define SIG_TEST 44
#define THREAD_WAKEUP  			0


#if 0
static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    ioctl_arg_t args;
    switch (cmd) {
        case IOCTL_SET_VARIABLES:
              if (copy_from_user(&args, (ioctl_arg_t *)arg, sizeof(ioctl_arg_t))) return -EACCES;
              pid = args.pid;
        break;
#endif

typedef struct signal_thread_s
{
	wait_queue_head_t	  wqueue;
	unsigned long		  flags;
	struct task_struct*   task;
	unsigned long		  timeout;
	void (*run)(void* arg);
}signal_thread_t;

static signal_thread_t g_st;

static void send_to_user_d(void* arg)
{
	struct siginfo info;
	struct task_struct *t;

	memset(&info, 0, sizeof(struct siginfo));
	info.si_signo = SIG_TEST;
	// This is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,    and kernel space should use SI_KERNEL. 
	// But if SI_KERNEL is used the real_time data  is not delivered to the user space signal handler function. */
	info.si_code = SI_QUEUE;
	// real time signals may have 32 bits of data.
	info.si_int = 1234; // Any value you want to send
	rcu_read_lock();
	// find the task with that pid
	t = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
	if (t != NULL) {
		rcu_read_unlock();      
		if (send_sig_info(SIG_TEST, &info, t) < 0) // send signal
			printk("send_sig_info error\n");
	} else {
		 printk("pid_task error\n");
		 rcu_read_unlock();
		//return -ENODEV;
	}

	msleep(1000);
	set_bit(THREAD_WAKEUP, &g_st.flags);
	wake_up(&g_st.wqueue);
}

static int signal_thread_fn(void* arg)
{
	signal_thread_t* st = arg;

	allow_signal(SIGKILL);
	while(!kthread_should_stop()) 
	{
		if(signal_pending(current))
		{
			flush_signals(current);
		}

		wait_event_interruptible_timeout (st->wqueue,
			 test_bit(THREAD_WAKEUP, &st->flags) || kthread_should_stop(), 
			 st->timeout);

		clear_bit(THREAD_WAKEUP, &st->flags);
		if(!kthread_should_stop())
		{
			st->run(st);
		}
	}

	return 0;
}

int __init hello_init(void)
{
	init_waitqueue_head(&g_st.wqueue);
	g_st.run = send_to_user_d;
	g_st.timeout = MAX_SCHEDULE_TIMEOUT;
	g_st.task = kthread_run(signal_thread_fn, &g_st, "signal_thread");
	if(IS_ERR(g_st.task))
	{
		printk("signal module init failed.\n");
		return -1;
	}
	printk("signal module init ok.\n");
	set_bit(THREAD_WAKEUP, &g_st.flags);
	wake_up(&g_st.wqueue);
    return 0;
}

void __exit hello_exit(void)
{
	kthread_stop(g_st.task);
    printk("hello exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
 
MODULE_LICENSE("GPL");
